package callback

import (
	"encoding/json"
	"gitee.com/injoyai/goutil/net/conn"
	"sync"
	"time"
)

var (
	// clientMap
	// 单例模式,储存成功的连接到链接池
	// 连接失败后自动删除,复用,削峰平谷
	clientMap = make(map[string]*Client)
	clientMu  sync.RWMutex
)

type Client struct {
	cli   *conn.Conn
	Error error
	wait  *Wait
}

// Debug 设置调试模式
func (this *Client) Debug(b ...bool) *Client {
	if this.Error == nil {
		this.cli.Debug(b...)
	}
	return this
}

// SetTimeout
// 设置等待超时时间
// 超过等待时间会返回超时错误
func (this *Client) SetTimeout(t time.Duration) *Client {
	if this.Error == nil {
		this.wait.SetTimeout(t)
	}
	return this
}

// Send
// 发送数据并等待响应,需要和Retry配合使用,
// 里面涉及固定的业务逻辑,或者后期单独出来
// @msgType,消息类型,
// @i,消息内容,结构默认转json格式
func (this *Client) Send(msgType string, i interface{}) *Msg {
	if this.Error != nil {
		return new(Msg).SetErr(this.Error)
	}
	m := NewMsg(msgType, toString(i))
	if err := this.cli.SendBytes(m.Bytes()); err != nil {
		return new(Msg).SetErr(err)
	}
	msg, err := this.wait.Wait(m.MsgID)
	if err != nil {
		return m.SetErr(err)
	}
	return msg
}

// NewClient 新建客户端,相同远程地址客户端复用,多个客户端通过num来增加
func NewClient(addr string, num ...int) *Client {
	no := ""
	if len(num) > 0 {
		no = toString(num[0])
	}
	clientMu.RLock()
	val, ok := clientMap[addr+no]
	clientMu.RUnlock()
	if ok && val.cli.Err() == nil {
		return val.Debug(false).SetTimeout(time.Second * 5)
	}
	c, err := conn.DialTCP(addr)
	data := &Client{
		cli:   c,
		Error: err,
		wait:  NewWait(time.Second * 5).SetReuse(),
	}
	if err == nil {
		c.SetPrintFunc(conn.PrintWithASCII)
		c.SetDealFunc(func(m *conn.Msg) {
			msg := new(Msg)
			msg.SetErr(json.Unmarshal(m.Bytes(), msg))
			data.wait.Done(msg.MsgID, msg)
		})
		c.SetCloseFunc(func(msg *conn.Msg) {
			clientMu.Lock()
			delete(clientMap, msg.Conn().GetRemoteAddr())
			clientMu.Unlock()
		})
		go func() {
			for {
				time.Sleep(time.Minute)
				if err := c.Send("ping"); err != nil {
					return
				}
			}
		}()
		go c.Run()
		clientMu.Lock()
		clientMap[addr+no] = data
		clientMu.Unlock()
	}
	return data
}
